/*-------------------------------------------------------------------------
 *
 * preptlist.c
 *	  预处理解析树目标列表的例程
 *
 * 对于 INSERT，目标列表必须包含目标关系中每个属性的正确顺序的条目。
 *
 * 对于 UPDATE，目标列表仅包含新列值的表达式。
 *
 * 对于 UPDATE 和 DELETE 查询，目标列表还必须包含允许执行器识别要更新或删除的行所需的“垃圾”目标列表条目；例如，堆行的 ctid。
 * （规划者添加这些；它们不在我们从规划者/重写器接收到的内容中。）
 *
 * 对于所有查询类型，可能还有其他垃圾目标列表条目，例如排序键、RETURNING 列表所需的变量，以及SELECT FOR UPDATE 锁定和/或 EvalPlanQual 检查所需的行 ID 信息。
 *
 * 查询重写阶段还会对目标列表进行预处理（见 rewriteTargetListIU）。这里和那里之间的工作划分部分是历史原因，但并不是完全随意的。
 * 这里完成的工作与对表的物理访问密切相关，而重写器的工作则更加关注 SQL 语义。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/prep/preptlist.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/table.h"
#include "nodes/makefuncs.h"
#include "optimizer/appendinfo.h"
#include "optimizer/optimizer.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
#include "utils/rel.h"

static List *fc_expand_insert_targetlist(PlannerInfo *fc_root, List *fc_tlist,
									  Relation fc_rel);


/*
 * preprocess_targetlist
 *	  预处理解析树目标列表的驱动程序。
 *
 * 预处理的目标列表返回在 root->processed_tlist 中。
 * 此外，如果这是一个 UPDATE，我们在 root->update_colnos 中返回
 * 一列目标列号。（processed_tlist 中的 Resnos 将连续，
 * 因此请勿查看该值以找出哪些列为目标！）
 */
void preprocess_targetlist(PlannerInfo *fc_root)
{
	Query	   *fc_parse = fc_root->parse;
	int			fc_result_relation = fc_parse->resultRelation;
	List	   *fc_range_table = fc_parse->rtable;
	CmdType		fc_command_type = fc_parse->commandType;
	RangeTblEntry *fc_target_rte = NULL;
	Relation	fc_target_relation = NULL;
	List	   *fc_tlist;
	ListCell   *fc_lc;

	/*
	 * 如果有结果关系，打开它以便我们可以查找缺失的
	 * 列等。我们假设之前的代码已经对该关系获得了
	 * 至少的 AccessShareLock，因此我们在这里不需要加锁。
	 */
	if (fc_result_relation)
	{
		fc_target_rte = rt_fetch(fc_result_relation, fc_range_table);

		/*
		 * 健全性检查：最好是一个真实的关系，而不是，比如说，一个子查询。
		 * 否则解析器或重写器出错。
		 */
		if (fc_target_rte->rtekind != RTE_RELATION)
			elog(ERROR, "result relation must be a regular relation");

		fc_target_relation = table_open(fc_target_rte->relid, NoLock);
	}
	else
		Assert(fc_command_type == CMD_SELECT);

	/*
	 * 在 INSERT 中，执行器期望目标列表与目标表的属性的确切
	 * 顺序相匹配，包括源查询中未提到的属性条目。
	 *
	 * 在 UPDATE 中，我们不重新排列目标列表的顺序，但需要
	 * 制作目标属性编号的单独列表，按目标列表的顺序，然后
	 * 重新编号 processed_tlist 条目以保持连续性。
	 */
	fc_tlist = fc_parse->targetList;
	if (fc_command_type == CMD_INSERT)
		fc_tlist = fc_expand_insert_targetlist(fc_root, fc_tlist, fc_target_relation);
	else if (fc_command_type == CMD_UPDATE)
		fc_root->update_colnos = extract_update_targetlist_colnos(fc_tlist);

	/*
	 * 对于非继承的 UPDATE/DELETE/MERGE，注册任何
	 * 垃圾列，以便让执行器能够识别要更新或
	 * 删除的行。在继承情况下，我们现在不做任何事情，
	 * 留待 expand_inherited_rtentry() 处理叶目标
	 * 关系时再进行。（但可能没有任何叶目标关系，
	 * 在这种情况下，我们必须在 distribute_row_identity_vars() 
	 * 中处理此问题。）
	 */
	if ((fc_command_type == CMD_UPDATE || fc_command_type == CMD_DELETE ||
		 fc_command_type == CMD_MERGE) &&
		!fc_target_rte->inh)
	{
		/* 行标识逻辑预计将数据添加到 processed_tlist */
		fc_root->processed_tlist = fc_tlist;
		add_row_identity_columns(fc_root, fc_result_relation,
								 fc_target_rte, fc_target_relation);
		fc_tlist = fc_root->processed_tlist;
	}

	/*
	 * 对于 MERGE，我们还需要分别处理每个 INSERT 和
	 * UPDATE 动作的目标列表。此外，我们检查每个
	 * 动作的 qual，并将任何在那里（除了目标关系的
	 * 变量）添加到子计划目标列表中。
	 */
	if (fc_command_type == CMD_MERGE)
	{
		ListCell   *fc_l;

		/*
		 * 对于 MERGE，分别处理每个 MergeAction 的目标列表。
		 * 对 MergeAction->targetList 进行与普通 INSERT
		 * 相同的处理。对于 UPDATE，收集正在修改的列号。
		 */
		foreach(fc_l, fc_parse->mergeActionList)
		{
			MergeAction *fc_action = (MergeAction *) lfirst(fc_l);
			List	   *fc_vars;
			ListCell   *fc_l2;

			if (fc_action->commandType == CMD_INSERT)
				fc_action->targetList = fc_expand_insert_targetlist(fc_root,
															  fc_action->targetList,
															  fc_target_relation);
			else if (fc_action->commandType == CMD_UPDATE)
				fc_action->updateColnos =
					extract_update_targetlist_colnos(fc_action->targetList);

			/*
			 * 为每个动作的目标列表和 WHEN 条件中使用的任何 Vars 和
			 * PlaceHolderVars 添加 resjunk 条目，这些变量属于
			 * 目标以外的关系。我们不期望在这里看到任何
			 * 聚合或窗口函数。
			 */
			fc_vars = pull_var_clause((Node *)
								   list_concat_copy((List *) fc_action->qual,
													fc_action->targetList),
								   PVC_INCLUDE_PLACEHOLDERS);
			foreach(fc_l2, fc_vars)
			{
				Var		   *fc_var = (Var *) lfirst(fc_l2);
				TargetEntry *fc_tle;

				if (IsA(fc_var, Var) && fc_var->varno == fc_result_relation)
					continue;	/* 不需要它 */

				if (tlist_member((Expr *) fc_var, fc_tlist))
					continue;	/* 已经得到了 */

				fc_tle = makeTargetEntry((Expr *) fc_var,
									  list_length(fc_tlist) + 1,
									  NULL, true);
				fc_tlist = lappend(fc_tlist, fc_tle);
			}
			list_free(fc_vars);
		}
	}

	
/*
	 * 为行标记的关系添加必要的垃圾列。这些值在选择 FOR UPDATE/SHARE 的关系锁定时是必需的，并进行 EvalPlanQual 重新检查。有关 PlanRowMark 的评论，请参见 plannodes.h。如果您修改此段落，请参阅 expand_inherited_rtentry()，它必须能够添加相当于这些的垃圾列。
	 *
	 * （有一天，将这些 resjunk 列合并到用于 UPDATE/DELETE 的行标识列管理中可能会很有用。然而，今天不是那一天。一个显著的问题是，这里创建的整个行 Vars 似乎重要的是使用真实的表行类型，而不是 RECORD，以便进行与子关系行类型的转换。另外，由于这些条目不会随着更多子关系而膨胀，因此实际上不太需要列共享。）
	 */
	foreach(fc_lc, fc_root->rowMarks)
	{
		PlanRowMark *fc_rc = (PlanRowMark *) lfirst(fc_lc);
		Var		   *fc_var;
		char		fc_resname[32];
		TargetEntry *fc_tle;

		/* 子关系使用与其父关系相同的垃圾属性 */
		if (fc_rc->rti != fc_rc->prti)
			continue;

		if (fc_rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
		{
			/* 需要获取 TID */
			fc_var = makeVar(fc_rc->rti,
						  SelfItemPointerAttributeNumber,
						  TIDOID,
						  -1,
						  InvalidOid,
						  0);
			snprintf(fc_resname, sizeof(fc_resname), "ctid%u", fc_rc->rowmarkId);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_tlist) + 1,
								  pstrdup(fc_resname),
								  true);
			fc_tlist = lappend(fc_tlist, fc_tle);
		}
		if (fc_rc->allMarkTypes & (1 << ROW_MARK_COPY))
		{
			/* 需要将整个行作为废变量 */
			fc_var = makeWholeRowVar(rt_fetch(fc_rc->rti, fc_range_table),
								  fc_rc->rti,
								  0,
								  false);
			snprintf(fc_resname, sizeof(fc_resname), "wholerow%u", fc_rc->rowmarkId);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_tlist) + 1,
								  pstrdup(fc_resname),
								  true);
			fc_tlist = lappend(fc_tlist, fc_tle);
		}

		/* 如果是继承树的父节点，始终也需获取 tableoid。 */
		if (fc_rc->isParent)
		{
			fc_var = makeVar(fc_rc->rti,
						  TableOidAttributeNumber,
						  OIDOID,
						  -1,
						  InvalidOid,
						  0);
			snprintf(fc_resname, sizeof(fc_resname), "tableoid%u", fc_rc->rowmarkId);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_tlist) + 1,
								  pstrdup(fc_resname),
								  true);
			fc_tlist = lappend(fc_tlist, fc_tle);
		}
	}

	/*
	 * 如果查询有 RETURNING 列表，为任何在 RETURNING 中使用的属于其他关系的 Vars 添加 resjunk 条目。我们需要这么做
	 * 以便使这些 Vars 可用于 RETURNING 计算。属于结果关系的 Vars 则不需要添加，因为它们将被
	 * 使其引用实际的堆元组。
	 */
	if (fc_parse->returningList && list_length(fc_parse->rtable) > 1)
	{
		List	   *fc_vars;
		ListCell   *fc_l;

		fc_vars = pull_var_clause((Node *) fc_parse->returningList,
							   PVC_RECURSE_AGGREGATES |
							   PVC_RECURSE_WINDOWFUNCS |
							   PVC_INCLUDE_PLACEHOLDERS);
		foreach(fc_l, fc_vars)
		{
			Var		   *fc_var = (Var *) lfirst(fc_l);
			TargetEntry *fc_tle;

			if (IsA(fc_var, Var) &&
				fc_var->varno == fc_result_relation)
				continue;		/* 不需要它 */

			if (tlist_member((Expr *) fc_var, fc_tlist))
				continue;		/* 已经得到了 */

			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_tlist) + 1,
								  NULL,
								  true);

			fc_tlist = lappend(fc_tlist, fc_tle);
		}
		list_free(fc_vars);
	}

	fc_root->processed_tlist = fc_tlist;

	if (fc_target_relation)
		table_close(fc_target_relation, NoLock);
}

/*
 * extract_update_targetlist_colnos
 * 		提取 UPDATE 的目标列表想要分配的目标表列号列表，然后重新编号。
 *
 * 在解析器和重写器中的约定是，UPDATE 的非 resjunk TLE 条目的 resnos 是要分配的目标列号。
 * 在这里，我们将该信息提取到一个单独的列表中，然后将 tlist 转换为所有其他查询类型使用的顺序编号约定。
 *
 * 这也适用于与 INSERT ... ON CONFLICT ... UPDATE 相关的 tlist，尽管是在计划的较晚阶段才进行。
 */
List * extract_update_targetlist_colnos(List *fc_tlist)
{
	List	   *update_colnos = NIL;
	AttrNumber	fc_nextresno = 1;
	ListCell   *fc_lc;

	foreach(fc_lc, fc_tlist)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_lc);

		if (!fc_tle->resjunk)
			update_colnos = lappend_int(update_colnos, fc_tle->resno);
		fc_tle->resno = fc_nextresno++;
	}
	return update_colnos;
}


/*****************************************************************************
 *
 *		目标列表扩展
 *
 *****************************************************************************/

/*
 * expand_insert_targetlist
 *	  给定由解析器生成的目标列表和结果关系，
 *	  为任何缺失的属性添加目标列表条目，并确保非废属性按正确字段顺序出现。
 *
 * 从前我们也大致如此处理 UPDATE 目标列表，
 * 但现在此代码仅适用于 INSERT 目标列表。
 */
static List * fc_expand_insert_targetlist(PlannerInfo *fc_root, List *fc_tlist, Relation fc_rel)
{
	List	   *fc_new_tlist = NIL;
	ListCell   *fc_tlist_item;
	int			fc_attrno,
				fc_numattrs;

	fc_tlist_item = list_head(fc_tlist);

	/*
	 * 重写器应该已经确保 TLE 处于正确顺序；
	 * 但我们必须插入任何缺失属性的 TLE。
	 *
	 * 扫描关系的 relcache 条目中的元组描述，以确保
	 * 我们以正确的顺序拥有所有用户属性。
	 */
	fc_numattrs = RelationGetNumberOfAttributes(fc_rel);

	for (fc_attrno = 1; fc_attrno <= fc_numattrs; fc_attrno++)
	{
		Form_pg_attribute fc_att_tup = TupleDescAttr(fc_rel->rd_att, fc_attrno - 1);
		TargetEntry *fc_new_tle = NULL;

		if (fc_tlist_item != NULL)
		{
			TargetEntry *fc_old_tle = (TargetEntry *) lfirst(fc_tlist_item);

			if (!fc_old_tle->resjunk && fc_old_tle->resno == fc_attrno)
			{
				fc_new_tle = fc_old_tle;
				fc_tlist_item = lnext(fc_tlist, fc_tlist_item);
			}
		}

		if (fc_new_tle == NULL)
		{
			/*
			 * 没有找到匹配的 tlist 条目，于是创建一个。
			 *
			 * 在这种情况下，INSERT 应该插入 NULL。（我们假设
			 * 重写器会插入任何可用的非 NULL 默认值。）此外，如果列没有被删除，则应用可能存在的任何域约束——
			 * 这是为了捕获域 NOT NULL。
			 *
			 * 当为已删除列生成 NULL 常量时，我们将其标记为 INT4（任何其他保证存在的数据类型也可以）。我们不能标记为已删除列的数据类型
			 * ，因为该类型可能不再存在。我们声称的类型实际上并不重要，因为 NULL 就是 NULL——它的
			 * 表示与数据类型无关。然而，这可能会让比较已完成计划和目标
			 * 关系的代码感到困惑。
			 */
			Node	   *fc_new_expr;

			if (!fc_att_tup->attisdropped)
			{
				fc_new_expr = coerce_null_to_domain(fc_att_tup->atttypid,
												 fc_att_tup->atttypmod,
												 fc_att_tup->attcollation,
												 fc_att_tup->attlen,
												 fc_att_tup->attbyval);
				/* 必须对任何非常量节点运行表达式预处理 */
				if (!IsA(fc_new_expr, Const))
					fc_new_expr = eval_const_expressions(fc_root, fc_new_expr);
			}
			else
			{
				/* 为已删除列插入 NULL */
				fc_new_expr = (Node *) makeConst(INT4OID,
											  -1,
											  InvalidOid,
											  sizeof(int32),
											  (Datum) 0,
											  true, /* isnull */
											  true /* byval */ );
			}

			fc_new_tle = makeTargetEntry((Expr *) fc_new_expr,
									  fc_attrno,
									  pstrdup(NameStr(fc_att_tup->attname)),
									  false);
		}

		fc_new_tlist = lappend(fc_new_tlist, fc_new_tle);
	}

	/*
	 * 剩余的 tlist 条目应为 resjunk；将它们全部附加到新的 tlist 末尾，确保它们的 resnos 高于最后一个
	 * 真实属性。（注意：尽管重写器已经进行了这样的
	 * 重新编号，但我们在这里必须再次进行，以防我们上面添加了 NULL 条目。）
	 */
	while (fc_tlist_item)
	{
		TargetEntry *fc_old_tle = (TargetEntry *) lfirst(fc_tlist_item);

		if (!fc_old_tle->resjunk)
			elog(ERROR, "targetlist is not sorted correctly");
		/* 正确获取 resno，但不要不必要地复制 */
		if (fc_old_tle->resno != fc_attrno)
		{
			fc_old_tle = flatCopyTargetEntry(fc_old_tle);
			fc_old_tle->resno = fc_attrno;
		}
		fc_new_tlist = lappend(fc_new_tlist, fc_old_tle);
		fc_attrno++;
		fc_tlist_item = lnext(fc_tlist, fc_tlist_item);
	}

	return fc_new_tlist;
}


/*
 * 定位给定 RT 索引的 PlanRowMark，如果没有则返回 NULL
 *
 * 这可能应该放在其他地方，但没有非常合适的位置
 */
PlanRowMark *
get_plan_rowmark(List *fc_rowmarks, Index fc_rtindex)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_rowmarks)
	{
		PlanRowMark *fc_rc = (PlanRowMark *) lfirst(fc_l);

		if (fc_rc->rti == fc_rtindex)
			return fc_rc;
	}
	return NULL;
}
